-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CONSULT-469] - introduce macro for PGN registration and NmeaMessage type #408
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of thoughts and questions on this one
micro-rdk-nmea/src/messages/pgns.rs
Outdated
|
||
impl NmeaMessage { | ||
pub fn new(mut bytes: Vec<u8>) -> Result<Self, NmeaParseError> { | ||
let msg_data = bytes.split_off(32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 32
should be a constant somewhere, and replaced globally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping?
micro-rdk-nmea/src/messages/pgns.rs
Outdated
} | ||
|
||
impl NmeaMessage { | ||
pub fn new(mut bytes: Vec<u8>) -> Result<Self, NmeaParseError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this takes the Vec
by value rather than just viewing the data, since ultimately, everything gets parsed out, shouldn't this be able to take something like a byte slice, all the way down?
Should this implement TryInto
at the top level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the first question, but I prefer dealing with a vec rather than a byte slice where I would have to consider the lifetime of a higher level piece of data.
I can make this TryFrom<Vec<u8>>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do see what you are saying about the virality of lifetime when dealing with byte slices, but, on the other hand, Vec
is saying that this method needs a uniquely owned buffer of data in order to convert it into an NmeaMessage
. That seems like it shouldn't be needed. If I have a bunch of bytes that I read into a buffer off the wire or bus that is, say, already in a Vec, I'd expect the parser to just be able to consume that data by read-only reference, rather than needing to copy it into a Vec just to call this method. On the other hand, I don't want to delay this work further. Would you be OK with filing a follow-up ticket?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I previously had DataCursor
work this way, but then @npmenard said it should own the data. If you can convince him this is better when he gets back, I'll make the ticket.
My opinion on this is not strong but I don't really see a use-case outside of debugging to want a read-only reference to the data meant to be converted. I can see a use case for maybe supporting any type that implements Read. I could make a ticket for that if you agree
micro-rdk-nmea/src/lib.rs
Outdated
@@ -26,11 +29,10 @@ mod tests { | |||
let mut data = Vec::<u8>::new(); | |||
let res = general_purpose::STANDARD.decode_vec(water_depth_str, &mut data); | |||
assert!(res.is_ok()); | |||
let cursor = DataCursor::new(data[33..].to_vec()); | |||
let message = WaterDepth::from_cursor(cursor, 13); | |||
let cursor = DataCursor::new(data[32..].to_vec()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, before, what was happening by starting at byte 33?
Also, is there a reason in this test and the others not to interpret the metadata in the 32 bytes at the front? It seems worthwhile to test that it parses correctly.
Finally, it looks like WaterDepth
still does have a source_id
, so, I'm curious why the value is no longer being validated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Byte 32 was the source_id for those messages that have one, but not every message has a source_id field. I can add checks in the tests for the ones that do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is worth checking the source_id is decoded correctly for those types that have them.
I also think it is worth parsing the metadata here and validating the decoded values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the check of the source_id
. Did you disagree with the idea to validate the metadata too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot about that one, I'd prefer to give it its own unit test rather than add it to all of these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel too strongly about it. I was suggesting it mostly because you already have static test data here, but the first 32 bytes get thrown away, when it could be a perfectly good test of decoding the metadata. If you do a separate test then you would just need to obtain the metadata bytes from somewhere anyway. Up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait I do have a unit test for this actually, does this really matter for every message type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Maybe just do one of them, if you want to.
micro-rdk-nmea/src/messages/pgns.rs
Outdated
} | ||
|
||
impl NmeaMessage { | ||
pub fn new(mut bytes: Vec<u8>) -> Result<Self, NmeaParseError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do see what you are saying about the virality of lifetime when dealing with byte slices, but, on the other hand, Vec
is saying that this method needs a uniquely owned buffer of data in order to convert it into an NmeaMessage
. That seems like it shouldn't be needed. If I have a bunch of bytes that I read into a buffer off the wire or bus that is, say, already in a Vec, I'd expect the parser to just be able to consume that data by read-only reference, rather than needing to copy it into a Vec just to call this method. On the other hand, I don't want to delay this work further. Would you be OK with filing a follow-up ticket?
micro-rdk-nmea/src/lib.rs
Outdated
@@ -26,11 +29,10 @@ mod tests { | |||
let mut data = Vec::<u8>::new(); | |||
let res = general_purpose::STANDARD.decode_vec(water_depth_str, &mut data); | |||
assert!(res.is_ok()); | |||
let cursor = DataCursor::new(data[33..].to_vec()); | |||
let message = WaterDepth::from_cursor(cursor, 13); | |||
let cursor = DataCursor::new(data[32..].to_vec()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is worth checking the source_id is decoded correctly for those types that have them.
I also think it is worth parsing the metadata here and validating the decoded values.
micro-rdk-nmea/src/messages/pgns.rs
Outdated
define_pgns!( | ||
(WaterDepth, Pgn128267, 128267), | ||
(TemperatureExtendedRange, Pgn130316, 130316), | ||
(GnssSatsInView, Pgn129540, 129540) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the enum variants looking like Pgn130316
or Pgn129540
, this enum is somewhat illegible to a user, since they need to know the PGN code to match, rather than the human readable name like GnssSatsInView
. Is it necessary to have the PGN number in the enum variants? Or could they be named much like the wrapped value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is very close to ready to approve. This pass is mostly only small things or questions/suggestions, and some naming nits.
} | ||
_ => { | ||
return Err(error_tokens( | ||
"bits received unexpected attribute value", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pgn instead of bits, I think.
micro-rdk-nmea/src/messages/pgns.rs
Outdated
let metadata = NmeaMessageMetadata::try_from(value)?; | ||
let data = MessageData::from_bytes(metadata.pgn(), msg_data)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like both these types (NmeaMessageMetadata
and MessageData
) should either start with Nmea
, or not. Given that there is also NmeaMessage, maybe that breaks the tie in favor of NmeaMessageData
?
micro-rdk-nmea/src/messages/pgns.rs
Outdated
@@ -106,3 +124,101 @@ pub struct GnssSatsInView { | |||
#[length_field = "sats_in_view"] | |||
satellites: Vec<Satellite>, | |||
} | |||
|
|||
macro_rules! define_pgns { | |||
( $(($pgndef:ident, $enum:ident, $pgn:expr)),* ) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you still need $pgn?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with one last annoying question.
(WaterDepth, Pgn128267, 128267), | ||
(TemperatureExtendedRange, Pgn130316, 130316), | ||
(GnssSatsInView, Pgn129540, 129540) | ||
(WaterDepth, 128267), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last question, I promise: Do you even need the PGN code as the second argument anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I cannot use information only available at runtime to generate the content of the match statement for the from_bytes
function
There was some confusion around source id that was cleared up with further testing. It turns out that source_id from some messages and source from the message metadata do not necessarily match (and that some messages do not even have a source_id). This PR contains the subsequent fix